package platform.app;

import common.struct.ConstantEnum.ServiceType;
import common.struct.AppInfo;
import com.sun.tools.javac.Main;
import common.struct.CtxServiceConfig;
import common.struct.State;
import common.struct.ConstantEnum.CtxValidator;
import common.struct.ServiceInfo;
import common.struct.sync.SynchronousString;
import common.util.Util;
import platform.database.Database;
import platform.pubsub.Channel;
import platform.resource.ActorMgr;
import platform.resource.ResMgrThread;
import platform.resource.SensorMgr;
import platform.service.ctx.ctxServer.AppCtxServer;
import platform.service.inv.AppInvServer;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AppMgr {
    private String appName;
    private int grpId;
    private Set<SensorMgr> sensors = ConcurrentHashMap.newKeySet();
    private Set<ActorMgr> actors = ConcurrentHashMap.newKeySet();
    private AppDriver appDriver = null;
    private Database database = null;

    //ctxService related
    private CtxServiceConfig ctxServiceConfig = null;
    private AppCtxServer ctxServer = null;
    private boolean ctxServerOn = false;
    private String ruleFile;
    private String bfuncFile;
    private String patternFile;
    private String mfuncFile;
    private CtxValidator ctxValidator = CtxValidator.ECC_IMD;

    //invDaikon related
    private AppInvServer invServer = null;

    //request
    private Map<String, SynchronousString> requestMap = new ConcurrentHashMap<>();

    public Map<String, SynchronousString> getRequestMap() {
        return requestMap;
    }

    public void setCtxServiceConfig(CtxServiceConfig config) {
        ctxServiceConfig = config;
        String dir = "Resources/config/platform/ctx/" + appName;
        if (config.getRuleFileContent() != null) {
            Util.writeFileContent(dir, "rules_yellow.xml", config.getRuleFileContent(),  "//shuangyinhao");
            ruleFile = dir + "/rules_yellow.xml";
        }
        if (config.getPatternFileContent() != null) {
            Util.writeFileContent(dir, "patterns_yellow.xml", config.getPatternFileContent(), "//shuangyinhao");
            patternFile = dir + "/patterns_yellow.xml";
        }
        if (config.getBfuncFileContent() != null) {
            Util.writeFileContent(dir, "bfuncs.java", config.getBfuncFileContent(),  "//shuangyinhao");
            String[] args = new String[] {dir + "/bfuncs.java"};
            if (Main.compile(args) == 0) {
                bfuncFile = dir + "/bfuncs.class";
            }
        }
        if (config.getMfuncFileContent() != null) {
            Util.writeFileContent(dir, "mfuncs.java", config.getMfuncFileContent(), "//shuangyinhao");
            String[] args = new String[] {dir + "/mfuncs.java"};
            if (Main.compile(args) == 0) {
                mfuncFile = dir + "/mfuncs.class";
            }
        }
        if (config.getRfuncFileContent() != null) {
            //TODO
        }
        if (config.getCtxValidator() != null) {
            ctxValidator = config.getCtxValidator();
            ctxValidator = CtxValidator.ECC_IMD;
        }
    }

//    public AppMgr(JSONObject object) {
//        this.appName = object.getString("appName");
//        JSONArray subs = object.getJSONArray("subscribe");
//        for (int i = 0; i < subs.size(); i++) {
//            JSONObject sub = subs.getJSONObject(i);
//            subConfigs.add(new SubConfig(sub));
//        }
//        try {
//            JSONArray rss = object.getJSONArray("registerSensors");
//            for (int i = 0; i < rss.size(); i++) {
//                String rs = rss.getString(i);
//                registerSensor(rs);
//            }
//        } catch (Exception e) {
//
//        }
//    }

    public AppMgr(String appName) {
        this.appName = appName;
    }

    public int getGrpId() {
        return grpId;
    }

    public void setGrpId(int grpId) {
        this.grpId = grpId;
    }

    public String getAppName() {
        return appName;
    }

    public Database getDatabase() {
        if (database == null) {
            database = new Database(appName);
        }
        return database;
    }

    public void removeDatabase() {
        database = null;
    }

    public Set<SensorMgr> getSensors() {
        return sensors;
    }

    public List<String> getSensorsName() {
        List<String> ret = new ArrayList<>();
        sensors.forEach(config -> {
            ret.add(config.getSensorName());
        });
        return ret;
    }

    public boolean addSensor(SensorMgr sensorMgr){
        if(sensors.contains(sensorMgr)){
            return false;
        }
        else{
            sensors.add(sensorMgr);
            if(ctxServer != null){
                ctxServer.getItemManager().createIndexQue(Channel.getSensor(sensorMgr.getSensorName()));
            }
            return true;
        }
    }

    public boolean removeSensor(SensorMgr sensorMgr){
        if(sensors.contains(sensorMgr)){
            sensors.remove(sensorMgr);
            if(ctxServer != null){
                ctxServer.getItemManager().removeIndexQue(Channel.getSensor(sensorMgr.getSensorName()));
            }
            return true;
        }
        else{
            return false;
        }
    }

    public Set<ActorMgr> getActors() {
        return actors;
    }

    public List<String> getActorsName() {
        List<String> ret = new ArrayList<>();
        actors.forEach(config -> {
            ret.add(config.getActorName());
        });
        return ret;
    }

    public void addActor(ActorMgr actor) {
        this.actors.add(actor);
    }

    public void removeActor(ActorMgr actor) {
        this.actors.remove(actor);
    }

    public void registerActor(String... actors) {
        try {
            for (String actor : actors) {
                ActorMgr config = ResMgrThread.getActorMgrs().get(actor);
                addActor(config);
                config.addApp(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void cancelActor(String... actors) {
        try {
            for (String actor : actors) {
                ActorMgr config = ResMgrThread.getActorMgrs().get(actor);
                removeActor(config);
                config.removeApp(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //ctxService related
    public void setCtxServerOn(boolean ctxServerOn) {
        this.ctxServerOn = ctxServerOn;
    }

    public void setRuleFile(String ruleFile){
        this.ruleFile = ruleFile;
    }

    public void setBfuncFile(String bfuncFile) {
        this.bfuncFile = bfuncFile;
    }

    public void setPatternFile(String patternFile) {
        this.patternFile = patternFile;
    }

    public void setMfuncFile(String mfuncFile) {
        this.mfuncFile = mfuncFile;
    }

    public void setCtxValidator(CtxValidator ctxValidator) {
        this.ctxValidator = ctxValidator;
    }

    public AppCtxServer getCtxServer() {
        return ctxServer;
    }

    public boolean isCtxServerOn() {
        return ctxServerOn;
    }

    public String getRuleFile() {
        return ruleFile;
    }

    public String getBfuncFile() {
        return bfuncFile;
    }

    public String getPatternFile() {
        return patternFile;
    }

    public String getMfuncFile() {
        return mfuncFile;
    }

    public CtxValidator getCtxValidator() {
        return ctxValidator;
    }

    public boolean startCtxServer() {
        this.ctxServer = new AppCtxServer(this);
        this.ctxServer.init();
        for(SensorMgr sensorMgr : this.getSensors()){
            this.ctxServer.subscribe(Channel.getSensor(sensorMgr.getSensorName()), grpId, 1); // apps are 0, so it should
            // be 1.
            this.ctxServer.getItemManager().createIndexQue(Channel.getSensor(sensorMgr.getSensorName()));
        }
        this.ctxServer.start();
        this.ctxServerOn = true;
        return true;
    }

    public boolean resetCtxServer(){
        this.ctxServerOn = false;
        this.ctxServer.reset();
        this.ctxServerOn = true;
        return true;
    }

    public boolean stopCtxServer(){
        for(SensorMgr sensorMgr : this.getSensors()){
            this.ctxServer.unsubscribe(Channel.getSensor(sensorMgr.getSensorName())); // apps are 0, so it should be 1.
            this.ctxServer.getItemManager().removeIndexQue(Channel.getSensor(sensorMgr.getSensorName()));
        }
        this.ctxServer.stop();
        this.ctxServerOn = false;
        return true;
    }

    //invDaikon related
    public boolean isInvServerOn() {
        //TODO
        return invServer != null;
    }

    public AppInvServer getInvServer() {
        return invServer;
    }

    public void setInvServer(AppInvServer invServer) {
        this.invServer = invServer;
    }

    @Override
    public String toString() {
        return new AppInfo(appName, ctxServerOn, isInvServerOn(), State.ON, getSensorsName(), getActorsName()).toString();
    }

    public void setAppDriver(AppDriver appDriver) {
        this.appDriver = appDriver;
    }

    public AppDriver getAppDriver() {
        return appDriver;
    }

    public ServiceInfo getServiceInfo(ServiceType service) {
        switch (service) {
            case CTX:
//                System.out.println("bbb" + ctxServiceConfig.getBfuncFileContent());
                return new ServiceInfo(service, appName, ctxServerOn ? State.ON : State.OFF,
                        ctxServiceConfig, ctxServer == null ? null : ctxServer.getResult());
            case INV:
                return new ServiceInfo(service, appName, isInvServerOn() ? State.ON : State.OFF,
                        invServer == null ? null : invServer.getConfig(), invServer == null ? null : invServer.getResult());
            default:
                return null;
        }
    }
}
